"""
推荐系统API接口
"""
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from typing import List, Optional
from starlette.concurrency import run_in_threadpool
import asyncio
import logging

from fastapi import Header
from utils.database import get_db
from utils.recommendation_system import (
    UserBasedCF, ItemBasedCF, UserSegmentation, collect_user_behavior
)
from Models.travel.models import Attraction, UserBehavior, UserSegment
from Models.travel.schemas import AttractionInfo, RecommendationResponse
from Models.login.models import User
from utils import JWTUtils

router = APIRouter(prefix="/travel/recommendation", tags=["推荐系统"])
logger = logging.getLogger(__name__)

# 推荐系统超时时间（秒）
RECOMMENDATION_TIMEOUT = 10


def get_current_user_id(authorization: str = Header(None)):
    """从Authorization header获取当前用户ID"""
    if not authorization:
        return None
    try:
        # 提取Bearer token
        if authorization.startswith("Bearer "):
            token = authorization[7:]
        else:
            token = authorization
        
        payload = JWTUtils.decode_access_token(token)
        if payload and payload.get("user_id"):
            return payload["user_id"]
        return None
    except:
        return None


@router.post("/collect", summary="收集用户行为数据")
async def collect_behavior(
    attraction_id: int,
    behavior_type: str = "view",
    score: Optional[float] = None,
    user_id: Optional[int] = None,
    authorization: str = Header(None),
    db: Session = Depends(get_db)
):
    """
    收集用户行为数据（在商品详情页调用）
    
    - **attraction_id**: 景点ID
    - **behavior_type**: 行为类型（view, click, favorite, order, review）
    - **score**: 评分/权重（0-1），可选，如果不提供则根据行为类型自动生成
    - **user_id**: 用户ID（可选，如果提供了Authorization header则从token获取）
    """
    try:
        # 优先从Authorization header获取用户ID
        if authorization:
            current_user_id = get_current_user_id(authorization)
            if current_user_id:
                user_id = current_user_id
        
        if not user_id:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="需要登录"
            )
        
        # 验证景点是否存在
        attraction = db.query(Attraction).filter(
            Attraction.id == attraction_id,
            Attraction.is_active == True
        ).first()
        
        if not attraction:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="景点不存在"
            )
        
        # 收集行为数据
        behavior = await run_in_threadpool(
            collect_user_behavior,
            db,
            user_id,
            attraction_id,
            behavior_type,
            score
        )
        
        return {
            "success": True,
            "message": "行为数据收集成功",
            "behavior_id": behavior.id
        }
    
    except HTTPException:
        raise
    except Exception as e:
        import logging
        logger = logging.getLogger(__name__)
        logger.error(f"收集用户行为失败: {e}", exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"收集用户行为失败: {str(e)}"
        )


@router.get("/user-based", response_model=RecommendationResponse, summary="基于用户的协同过滤推荐")
async def get_user_based_recommendations(
    top_n: int = 3,
    user_id: Optional[int] = None,
    authorization: str = Header(None),
    db: Session = Depends(get_db)
):
    """
    基于用户的协同过滤推荐
    
    - **top_n**: 推荐数量（默认3）
    - **user_id**: 用户ID（可选，如果提供了Authorization header则从token获取）
    """
    try:
        # 优先从Authorization header获取用户ID
        if authorization:
            current_user_id = get_current_user_id(authorization)
            if current_user_id:
                user_id = current_user_id
        
        if not user_id:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="需要登录"
            )
        
        async def recommend_with_timeout():
            """带超时的推荐计算"""
            try:
                def recommend():
                    # 初始化推荐系统
                    cf = UserBasedCF(db)
                    if not cf.initialize(k_similar_users=3):
                        return []
                    
                    # 获取推荐
                    recommendations = cf.recommend(user_id, top_n=top_n)
                    return recommendations
                
                # 使用asyncio.wait_for添加超时
                recommendations = await asyncio.wait_for(
                    run_in_threadpool(recommend),
                    timeout=RECOMMENDATION_TIMEOUT
                )
                return recommendations
            except asyncio.TimeoutError:
                logger.warning(f"用户 {user_id} 的推荐计算超时（>{RECOMMENDATION_TIMEOUT}秒）")
                return []
            except Exception as e:
                logger.error(f"推荐计算失败: {e}", exc_info=True)
                return []
        
        recommendations = await recommend_with_timeout()
        
        if not recommendations:
            return RecommendationResponse(
                user_id=user_id,
                algorithm="user-based",
                attractions=[],
                message="暂无推荐，请先浏览一些景点"
            )
        
        # 获取景点详情
        attraction_ids = [item_id for item_id, _ in recommendations]
        attractions = db.query(Attraction).filter(
            Attraction.id.in_(attraction_ids),
            Attraction.is_active == True
        ).all()
        
        # 按推荐分数排序
        attraction_dict = {a.id: a for a in attractions}
        sorted_attractions = []
        for item_id, score in recommendations:
            if item_id in attraction_dict:
                sorted_attractions.append(attraction_dict[item_id])
        
        return RecommendationResponse(
            user_id=user_id,
            algorithm="user-based",
            attractions=[AttractionInfo.model_validate(a) for a in sorted_attractions],
            scores={item_id: score for item_id, score in recommendations if item_id in attraction_dict}
        )
    
    except HTTPException:
        raise
    except Exception as e:
        import logging
        logger = logging.getLogger(__name__)
        logger.error(f"获取推荐失败: {e}", exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"获取推荐失败: {str(e)}"
        )


@router.get("/item-based", response_model=RecommendationResponse, summary="基于物品的协同过滤推荐")
async def get_item_based_recommendations(
    top_n: int = 3,
    user_id: Optional[int] = None,
    authorization: str = Header(None),
    db: Session = Depends(get_db)
):
    """
    基于物品的协同过滤推荐
    
    - **top_n**: 推荐数量（默认3）
    - **user_id**: 用户ID（可选，如果提供了Authorization header则从token获取）
    """
    try:
        # 优先从Authorization header获取用户ID
        if authorization:
            current_user_id = get_current_user_id(authorization)
            if current_user_id:
                user_id = current_user_id
        
        if not user_id:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="需要登录"
            )
        
        async def recommend_with_timeout():
            """带超时的推荐计算"""
            try:
                def recommend():
                    # 初始化推荐系统
                    cf = ItemBasedCF(db)
                    if not cf.initialize(k_similar=3):
                        return []
                    
                    # 获取推荐
                    recommendations = cf.recommend(user_id, top_n=top_n)
                    return recommendations
                
                # 使用asyncio.wait_for添加超时
                recommendations = await asyncio.wait_for(
                    run_in_threadpool(recommend),
                    timeout=RECOMMENDATION_TIMEOUT
                )
                return recommendations
            except asyncio.TimeoutError:
                logger.warning(f"用户 {user_id} 的推荐计算超时（>{RECOMMENDATION_TIMEOUT}秒）")
                return []
            except Exception as e:
                logger.error(f"推荐计算失败: {e}", exc_info=True)
                return []
        
        recommendations = await recommend_with_timeout()
        
        if not recommendations:
            return RecommendationResponse(
                user_id=user_id,
                algorithm="item-based",
                attractions=[],
                message="暂无推荐，请先浏览一些景点"
            )
        
        # 获取景点详情
        attraction_ids = [item_id for item_id, _ in recommendations]
        attractions = db.query(Attraction).filter(
            Attraction.id.in_(attraction_ids),
            Attraction.is_active == True
        ).all()
        
        # 按推荐分数排序
        attraction_dict = {a.id: a for a in attractions}
        sorted_attractions = []
        for item_id, score in recommendations:
            if item_id in attraction_dict:
                sorted_attractions.append(attraction_dict[item_id])
        
        return RecommendationResponse(
            user_id=user_id,
            algorithm="item-based",
            attractions=[AttractionInfo.model_validate(a) for a in sorted_attractions],
            scores={item_id: score for item_id, score in recommendations if item_id in attraction_dict}
        )
    
    except HTTPException:
        raise
    except Exception as e:
        import logging
        logger = logging.getLogger(__name__)
        logger.error(f"获取推荐失败: {e}", exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"获取推荐失败: {str(e)}"
        )


@router.get("/segment", summary="用户分层分析")
async def get_user_segment(
    user_id: Optional[int] = None,
    authorization: str = Header(None),
    db: Session = Depends(get_db)
):
    """
    获取用户分层信息
    
    - **user_id**: 用户ID（可选，如果提供了Authorization header则从token获取）
    """
    try:
        # 优先从Authorization header获取用户ID
        if authorization:
            current_user_id = get_current_user_id(authorization)
            if current_user_id:
                user_id = current_user_id
        
        if not user_id:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="需要登录"
            )
        
        def analyze():
            segmentation = UserSegmentation(db)
            segment = segmentation.segment_user(user_id)
            features = segmentation.analyze_user(user_id)
            return segment, features
        
        segment, features = await run_in_threadpool(analyze)
        
        return {
            "user_id": user_id,
            "segment": segment,
            "features": features,
            "segment_name": {
                "vip": "高价值用户",
                "active": "活跃用户",
                "normal": "普通用户",
                "inactive": "不活跃用户"
            }.get(segment, "未知")
        }
    
    except HTTPException:
        raise
    except Exception as e:
        import logging
        logger = logging.getLogger(__name__)
        logger.error(f"获取用户分层失败: {e}", exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"获取用户分层失败: {str(e)}"
        )


@router.get("/hybrid", response_model=RecommendationResponse, summary="混合推荐（结合用户和物品协同过滤）")
async def get_hybrid_recommendations(
    top_n: int = 3,
    user_weight: float = 0.5,
    user_id: Optional[int] = None,
    authorization: str = Header(None),
    db: Session = Depends(get_db)
):
    """
    混合推荐：结合基于用户和基于物品的协同过滤
    
    - **top_n**: 推荐数量（默认3）
    - **user_weight**: 用户协同过滤的权重（0-1，默认0.5）
    - **user_id**: 用户ID（可选，如果提供了Authorization header则从token获取）
    """
    try:
        # 优先从Authorization header获取用户ID
        if authorization:
            current_user_id = get_current_user_id(authorization)
            if current_user_id:
                user_id = current_user_id
        
        if not user_id:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="需要登录"
            )
        
        async def recommend_with_timeout():
            """带超时的推荐计算"""
            try:
                def recommend():
                    # 获取两种推荐结果
                    user_cf = UserBasedCF(db)
                    item_cf = ItemBasedCF(db)
                    
                    user_recommendations = []
                    item_recommendations = []
                    
                    if user_cf.initialize(k_similar_users=3):
                        user_recommendations = user_cf.recommend(user_id, top_n=top_n * 2)
                    
                    if item_cf.initialize(k_similar=3):
                        item_recommendations = item_cf.recommend(user_id, top_n=top_n * 2)
                    
                    # 融合推荐结果
                    item_scores = {}
                    
                    # 用户协同过滤结果
                    for item_id, score in user_recommendations:
                        item_scores[item_id] = item_scores.get(item_id, 0) + score * user_weight
                    
                    # 物品协同过滤结果
                    item_weight = 1.0 - user_weight
                    for item_id, score in item_recommendations:
                        item_scores[item_id] = item_scores.get(item_id, 0) + score * item_weight
                    
                    # 排序并取Top-N
                    sorted_items = sorted(
                        item_scores.items(),
                        key=lambda x: x[1],
                        reverse=True
                    )[:top_n]
                    
                    return sorted_items
                
                # 使用asyncio.wait_for添加超时
                recommendations = await asyncio.wait_for(
                    run_in_threadpool(recommend),
                    timeout=RECOMMENDATION_TIMEOUT * 2  # 混合推荐需要更长时间
                )
                return recommendations
            except asyncio.TimeoutError:
                logger.warning(f"用户 {user_id} 的混合推荐计算超时（>{RECOMMENDATION_TIMEOUT * 2}秒）")
                return []
            except Exception as e:
                logger.error(f"混合推荐计算失败: {e}", exc_info=True)
                return []
        
        recommendations = await recommend_with_timeout()
        
        if not recommendations:
            return RecommendationResponse(
                user_id=user_id,
                algorithm="hybrid",
                attractions=[],
                message="暂无推荐，请先浏览一些景点"
            )
        
        # 获取景点详情
        attraction_ids = [item_id for item_id, _ in recommendations]
        attractions = db.query(Attraction).filter(
            Attraction.id.in_(attraction_ids),
            Attraction.is_active == True
        ).all()
        
        # 按推荐分数排序
        attraction_dict = {a.id: a for a in attractions}
        sorted_attractions = []
        for item_id, score in recommendations:
            if item_id in attraction_dict:
                sorted_attractions.append(attraction_dict[item_id])
        
        return RecommendationResponse(
            user_id=user_id,
            algorithm="hybrid",
            attractions=[AttractionInfo.model_validate(a) for a in sorted_attractions],
            scores={item_id: score for item_id, score in recommendations if item_id in attraction_dict}
        )
    
    except HTTPException:
        raise
    except Exception as e:
        import logging
        logger = logging.getLogger(__name__)
        logger.error(f"获取混合推荐失败: {e}", exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"获取混合推荐失败: {str(e)}"
        )

